use std::collections::HashMap;

/// vector 通过整型下标来存储值，而 HashMap（散列表）通过键（key）来存储值。
/// HashMap 的键可以是布尔型、整型、字符串，或任意实现了 Eq 和 Hash trait 的其他类型。
/// 在下一节将进一步介绍。
///
/// 和 vector 类似，HashMap 也是可增长的，但 HashMap 在占据了多余空间时还可以缩小自己。
/// 可以使用 HashMap::with_capacity(unit) 创建具有一定初始容量的 HashMap，
/// 也可以使用 HashMap::new() 来获得一个带有默认初始容量的 HashMap（这是推荐方式）。
///

///


fn call(number: &str) -> &str {
    match number {
        "798-1364" => {
            "We're sorry, the call cannot be completed as dialed.
            Please hang up and try again."
        }
        "645-7689" => {
            "Hello, this is Mr. Awesome's Pizza. My name is Fred.
            What can I get for you today?"
        }
        _ => "Hi! Who is this again?",
    }
}

#[allow(unused)]
#[test]
fn test_hashmap() {
    let mut contacts = HashMap::new();

    contacts.insert("Daniel", "798-1364");
    contacts.insert("Ashley", "645-7689");
    contacts.insert("Katie", "435-8291");
    contacts.insert("Robert", "956-1745");

    /// 接受一个引用并返回 Option<&V>
    match contacts.get(&"Daniel") {
        Some(&number) => println!("Calling Daniel: {}", call(number)),
        _ => println!("Don't have Daniel's number."),
    }

    /// 如果被插入的值为新内容，那么 `HashMap::insert()` 返回 `None`，
    /// 否则返回 `Some(value)`
    contacts.insert("Daniel", "164-6743");

    match contacts.get(&"Ashley") {
        Some(&number) => println!("Calling Ashley: {}", call(number)),
        _ => println!("Don't have Ashley's number."),
    }

    contacts.remove(&("Ashley"));

    /// `HashMap::iter()` 返回一个迭代器，该迭代器以任意顺序举出
    /// (&'a key, &'a value) 对。
    for (contact, &number) in contacts.iter() {
        println!("Calling {}: {}", contact, call(number));
    }
}

/// 任何实现了 Eq 和 Hash trait 的类型都可以充当 HashMap 的键。这包括：
///
/// - `bool` （当然这个用处不大，因为只有两个可能的键）
/// - `int`，`unit`，以及`其他整数类型`
/// - `String` 和 `&str`（友情提示：如果使用 String 作为键来创建 HashMap，
/// 则可以 将 &str 作为散列表的` .get() `方法的参数，以获取值）
///
/// 注意到 f32 和 f64 没有实现 Hash，这很大程度上是由于若使用浮点数作为散列表的键，
/// 浮点精度误差会很容易导致错误。
///
/// 对于所有的集合类（collection class），如果它们包含的类型都分别实现了 Eq 和 Hash，
/// 那么这些集合类也就实现了 Eq 和 Hash。例如，若 T 实现了 Hash，则 Vec<T> 也实现了 Hash。
///
/// 对自定义类型可以轻松地实现 Eq 和 Hash，只需加上一行代码：`#[derive(PartialEq, Eq, Hash)]`。
///
/// 编译器将会完成余下的工作。如果你想控制更多的细节，你可以手动实现 Eq 和/或 Hash。
/// 本指南不包含实现 Hash 的细节内容。
/// 为了试验 HashMap 中的 struct，让我们试着做一个非常简易的用户登录系统：

// Eq 要求你对此类型推导 PartiaEq。
#[derive(PartialEq, Eq, Hash)]
struct Account<'a>{
    username: &'a str,
    password: &'a str,
}

struct AccountInfo<'a>{
    name: &'a str,
    email: &'a str,
}

type Accounts<'a> = HashMap<Account<'a>, AccountInfo<'a>>;

fn try_logon<'a>(accounts: &Accounts<'a>,
                 username: &'a str, password: &'a str){
    println!("Username: {}", username);
    println!("Password: {}", password);
    println!("Attempting logon...");

    let logon = Account {
        username: username,
        password: password,
    };

    match accounts.get(&logon) {
        Some(account_info) => {
            println!("Successful logon!");
            println!("Name: {}", account_info.name);
            println!("Email: {}", account_info.email);
        },
        _ => println!("Login failed!"),
    }
}
#[allow(unused)]
#[test]
fn test_login() {
    let mut accounts: Accounts = HashMap::new();

    let account = Account {
        username: "j.everyman",
        password: "password123",
    };

    let account_info = AccountInfo {
        name: "John Everyman",
        email: "j.everyman@email.com",
    };

    accounts.insert(account, account_info);

    try_logon(&accounts, "j.everyman", "psasword123");

    try_logon(&accounts, "j.everyman", "password123");
}